{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import scipy.special"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "class NeuralNetwork:\n",
    "    # initialise the neural network\n",
    "    def __init__(self, input_nodes, hidden_nodes, output_nodes, learning_rate) -> None:\n",
    "        self.inodes = input_nodes\n",
    "        self.hnodes = hidden_nodes\n",
    "        self.onodes = output_nodes\n",
    "        self.lr = learning_rate\n",
    "\n",
    "        # set weight: input -> hidden, -0.5 ~ +0.5\n",
    "        # self.wih = np.random.rand(self.hnodes, self.inodes) - 0.5\n",
    "        self.wih = np.random.normal(0.0, pow(self.hnodes, -0.5), (self.hnodes, self.inodes))\n",
    "        # hidden -> output\n",
    "        # self.who = np.random.rand(self.onode, self.hnodes) - 0.5\n",
    "        self.who = np.random.normal(0.0, pow(self.onodes, -0.5), (self.onodes, self.hnodes))\n",
    "\n",
    "        # activation function: sigmoid = 1/(1+exp(-x))\n",
    "        self.activation_function = lambda x: scipy.special.expit(x)\n",
    "\n",
    "    # train the neural network\n",
    "    def train(self, input_list, target_list):\n",
    "        inputs = np.array(input_list, ndmin=2).T\n",
    "        targets = np.array(target_list, ndmin=2).T\n",
    "        hidden_inputs = np.dot(self.wih, inputs)\n",
    "        hidden_outputs = self.activation_function(hidden_inputs)\n",
    "        final_inputs = np.dot(self.who, hidden_outputs)\n",
    "        final_outputs = self.activation_function(final_inputs)\n",
    "\n",
    "        # calculate errors\n",
    "        output_errors = targets - final_outputs\n",
    "        hidden_errors = np.dot(self.who.T, output_errors)\n",
    "\n",
    "        # update weights\n",
    "        self.who += self.lr * np.dot(output_errors * final_outputs * (1.0 - final_outputs), np.transpose(hidden_outputs))\n",
    "        self.wih += self.lr * np.dot(hidden_errors * hidden_outputs * (1.0 - hidden_outputs), np.transpose(inputs))\n",
    "\n",
    "    # query the neural network\n",
    "    def query(self, input_list):\n",
    "        # convert input to 2d array\n",
    "        inputs = np.array(input_list, ndmin=2).T\n",
    "        hidden_inputs = np.dot(self.wih, inputs)\n",
    "        hidden_outputs = self.activation_function(hidden_inputs)\n",
    "        final_inputs = np.dot(self.who, hidden_outputs)\n",
    "        final_outputs = self.activation_function(final_inputs)\n",
    "        return final_outputs\n",
    "        "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "training_data_file = open('mnist_dataset/mnist_train.csv', 'r')\n",
    "training_data_list = training_data_file.readlines()\n",
    "training_data_file.close()\n",
    "# test results\n",
    "test_data_file = open('mnist_dataset/mnist_test.csv', 'r')\n",
    "test_data_list = test_data_file.readlines()\n",
    "test_data_file.close()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "def do_train_and_test(input_nodes, hidden_nodes, output_nodes, learning_rate, correct_rates, epochs):\n",
    "    n = NeuralNetwork(input_nodes, hidden_nodes, output_nodes, learning_rate)\n",
    "    \n",
    "    for epoch in range(epochs):\n",
    "        for record in training_data_list:\n",
    "            all_values = record.split(',')\n",
    "            inputs = np.asfarray(all_values[1:]) / 255.0 * 0.99 + 0.01\n",
    "            targets = np.zeros(output_nodes) + 0.01\n",
    "            targets[int(all_values[0])] = 0.99\n",
    "            n.train(inputs, targets)\n",
    "    # record scores\n",
    "    score_card = []\n",
    "    for record in test_data_list:\n",
    "        all_values = record.split(',')\n",
    "        correct_label = int(all_values[0])\n",
    "        inputs = np.asfarray(all_values[1:]) / 255.0 * 0.99 + 0.01\n",
    "        outputs = n.query(inputs)\n",
    "        # get index of max value \n",
    "        label = np.argmax(outputs)\n",
    "        if label == correct_label:\n",
    "            score_card.append(1)\n",
    "        else:\n",
    "            score_card.append(0)\n",
    "    score_card_array = np.asarray(score_card)\n",
    "    correct_rates[learning_rate] = score_card_array.sum() / score_card_array.size"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [],
   "source": [
    "epochs = 2\n",
    "learning_rate = 0.2\n",
    "input_nodes = 784 # image is 28 x 28\n",
    "hidden_nodes = 100\n",
    "output_nodes = 10 # 0 ~ 9 total 10 labels\n",
    "correct_rates = {}\n",
    "do_train_and_test(input_nodes, hidden_nodes, output_nodes, learning_rate, correct_rates, epochs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{0.2: 0.9624}"
      ]
     },
     "execution_count": 37,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "correct_rates"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{0.01: 0.9213,\n",
       " 0.1: 0.9474,\n",
       " 0.2: 0.9478,\n",
       " 0.3: 0.94,\n",
       " 0.4: 0.9241,\n",
       " 0.5: 0.9181,\n",
       " 0.6: 0.8993,\n",
       " 0.7: 0.896,\n",
       " 0.8: 0.8887,\n",
       " 0.9: 0.8423}"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "learning_rate = 0.1\n",
    "input_nodes = 784 # image is 28 x 28\n",
    "hidden_nodes = 100\n",
    "output_nodes = 10 # 0 ~ 9 total 10 labels\n",
    "correct_rates = {}\n",
    "\n",
    "learning_rates = [0.01, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]\n",
    "for learning_rate in learning_rates:\n",
    "    do_train_and_test(input_nodes, hidden_nodes, output_nodes, learning_rate, correct_rates)\n",
    "    print(f'learning_rate: {learning_rate}')\n",
    "correct_rates"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "plt.scatter(correct_rates.keys(), correct_rates.values())\n",
    "plt.plot(correct_rates.keys(), correct_rates.values())\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3.8.13 ('ai')",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.13"
  },
  "orig_nbformat": 4,
  "vscode": {
   "interpreter": {
    "hash": "0cb0046d74bfb5a9fee6ebb85b9850d762e40d6b321d5d9a0aa0313eefa3c5b4"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
